package com.funcity.resource
{
	import de.polygonal.ds.Itr;
	import de.polygonal.ds.ListSet;
	import de.polygonal.ds.PriorityQueue;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.Dictionary;
	
	
	/**
	 * 这个loader只能供某一类同一回调函数调用（这里是rescacher），因为回调函数在构造函数定死了。
	 * 每次load时指明回调函数就能通用了。
	 */	
	public final class ResourceLoader
	{
		private static const POOL_SIZE:uint = 8;
		private static const PENDING_SIZE:uint = 64;
		
		private var _loaderPool:Dictionary = new Dictionary();
		private var _loading:ListSet = new ListSet();
		private var _urlToPE:Dictionary = new Dictionary();
		private var _pending:PriorityQueue = new PriorityQueue(false, PENDING_SIZE);
		private var _urlRoot:String;
		private var _loadHandler:Function;
		
		public function ResourceLoader(urlRoot:String, loadHandler:Function)
		{
			for (var idx:uint = 0; idx < POOL_SIZE; idx++)
			{
				var loader:URLLoader = new URLLoader();
				loader.dataFormat = URLLoaderDataFormat.BINARY;
				
				loader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
				loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler);
				loader.addEventListener(Event.COMPLETE, completeHandler);
				
				_loaderPool[loader] = null;
			}
			_urlRoot = urlRoot;
			_loadHandler = loadHandler;
		}
		
		public function cancel(url:String):Boolean
		{
			//没在下载的
			if(_loading.has(url) == false)
			{
				return true;
			}
			//等待下载的
			var pe:PendingEntry = _urlToPE[url];
			if(pe != null)
			{
				_loading.remove(url);
				_pending.remove(pe);
				delete _urlToPE[url];
				return true;
			}
			//正在下载的不处理,回调的人自己处理
			return false;
		}
		
		public function load(url:String):void
		{
			if (_loading.has(url))
			{
				return;
			}
			_loading.set(url);
			
			var loader:URLLoader = getLoader(url);
			if (loader)
			{
				loader.load(new URLRequest(_urlRoot + url));
			}
			else
			{
				var pe:PendingEntry = new PendingEntry(url);
				_urlToPE[url] = pe;
				_pending.enqueue(pe);
			}
		}
		
		private function getLoader(url:String):URLLoader
		{
			for (var loader:Object in _loaderPool)
			{
				if (_loaderPool[loader] == null)
				{
					_loaderPool[loader] = url;
					return URLLoader(loader);
				}
			}
			return null;
		}
		
		private function finishLoading(loader:URLLoader):String
		{
			var url:String = _loaderPool[loader];
			_loading.remove(url);
			return url;
		}
		
		private function loadNext(loader:URLLoader):void
		{
			_loaderPool[loader] = null; // free loader
			
			var iter:Itr = _pending.iterator();
			if (iter.hasNext())
			{
				var url:String = iter.next().url;
				_pending.dequeue();
				delete _urlToPE[url];
				_loaderPool[loader] = url;
				loader.load(new URLRequest(_urlRoot + url));
			}
		}
		
		private function errorHandler(e:Event):void
		{
			var loader:URLLoader = URLLoader(e.target);
			var url:String = finishLoading(loader);
			
			_loadHandler(url, null); // call handler
			loadNext(loader); // load next one
		}
		
		private function completeHandler(e:Event):void 
		{
			var loader:URLLoader = URLLoader(e.target);
			var url:String = finishLoading(loader);

			_loadHandler(url, loader.data); // call handler
			loadNext(loader); // load next one
		}
	}
}

import de.polygonal.ds.Prioritizable;

final class PendingEntry implements Prioritizable
{
	private static const CATEGORY_PRIORITY:Object = { 
		map:7, character:6, weapon:5, icon:4, effect:3, asset:2, music:1 
	};
	
	public var priority:int;
	public var position:int;
	
	private var _url:String;
	
	public function PendingEntry(url:String)
	{
		_url = url;
		priority = getPriority();
	}
	
	public function get url():String
	{
		return _url;
	}
	
	private function getPriority():int
	{
		var category:String =  _url.substring(0, _url.indexOf("/"));
		return CATEGORY_PRIORITY[category] ? CATEGORY_PRIORITY[category] : 0;
	}
}